import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;

import com.colloquial.arithcode.ArithDecoder;
import com.colloquial.arithcode.ArithEncoder;
import com.colloquial.arithcode.BitInput;
import com.colloquial.arithcode.BitOutput;

import entrada.EasyIn;



/*
 * fiz a parte tranquila at� agora, vou tentar fazer a descompressao tbm
 * depois se passar pelos 3 m�todos que tem por ai, no main, j� deve estar tudo
 * operacional. (fiz uma mudan�a no codificador, para qndo o decodificador estiver
 * funcionando ele n�o vai codificar nada... fechado?? )
 */

public class Decodificador {

	private static Decodificador instanceOfDecodificador;

	private InputStream inBits;

	private FileOutputStream outStream;

	private ArithDecoder decodificador;

	private int contextoMaximo;

	private boolean decodificadorManda = false;

	private No p;

	private Decodificador() {
		p = new No("raiz");
	}

	public static Decodificador getInstance() {
		if(instanceOfDecodificador == null) {
			instanceOfDecodificador = new Decodificador();
		}
		return instanceOfDecodificador;
	}


	public int decodificar(int totalCount) {
		return decodificador.getCurrentSymbolCount(totalCount);
	}



	public void retirarSimbolo(int []dados) {
		try {
			decodificador.removeSymbolFromStream(dados);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public boolean close(){
		try {
			this.decodificador.close();

		} catch (IOException e) {
			return false;
		}
		return true;
	}

	public boolean decodificadorManda() {
		return this.decodificadorManda;
	}

	// FUCkinG Around

	/**
	 * 
	 */
	private void decodificarbb() {

		this.decodificadorManda = true;

	/*	p.adiciona("a");
		p.adiciona("ab");
		p.adiciona("abc");
*/
		p.exibir("");
		
		System.out.println(TabelaIgnorancia.getInstance().getTotalSimbolosNaoDecodificados());
		
		char simbol = TabelaIgnorancia.getInstance().descobreSimbolo(97);
		System.out.println(simbol);
		TabelaIgnorancia.getInstance().probSimboloDec(simbol);
		simbol = TabelaIgnorancia.getInstance().descobreSimbolo(97);
		System.out.println(simbol);

		System.out.println(TabelaIgnorancia.getInstance().getTotalSimbolosNaoDecodificados());

//		System.out.println("valor decodificado " + p.totalDeSimbolosNoContexto() );

		/*
		System.out.println("0 - " + p.retornaSimbolo(0));
		System.out.println("1 - " + p.retornaSimbolo(1));
		System.out.println("2 - " + p.retornaSimbolo(2));
		System.out.println("3 - " + p.retornaSimbolo(3));
		System.out.println("4 - " + p.retornaSimbolo(4));
		System.out.println("5 - " + p.retornaSimbolo(5));
		System.out.println("6 - " + p.retornaSimbolo(6));*/

		No teste = p.retornarContexto("");
		if(teste == null) System.out.println("NULL");
		else {
			System.out.println("valor decodificado " + teste.getSimbolo());
		}
		/*
		while (this.decodificador.endOfStream() ){
			int valor = decodificar(TabelaIgnorancia.getInstance().getTotalSimbolosNaoDecodificados());

			char charValor = TabelaIgnorancia.getInstance().descobreSimbolo(valor);
			System.out.println(charValor);


			int [] valores = new int [3];
			valores[0] = valor;
			valores[1] = valor+1;
			valores[2] = TabelaIgnorancia.getInstance().getTotalSimbolosNaoDecodificados();		
			retirarSimbolo(valores);

			System.out.println(valor);		
			valor = decodificar(2);
			System.out.println(valor);

			valores[0] = valor;
			valores[1] = valor+1;
			valores[2] = 2;		

			retirarSimbolo(valores);

			valor = decodificar(TabelaIgnorancia.getInstance().getTotalSimbolosNaoDecodificados());
			charValor = TabelaIgnorancia.getInstance().descobreSimbolo(valor);
			System.out.println(charValor);

		}*/

	}



	private void decodificar() {

		this.decodificadorManda = true;

		String contexto = "";
		int tamanhoContextoAtivo = 0;
		int valorDecodificado = 0;
		int[] dados = null;

		char simbolo;
		String simbolo2 = "";
		String stringAux = "";

		No aux = null;

		while( !decodificador.endOfStream() ) {

			if(tamanhoContextoAtivo == 0){
				
				// ok! testado e validado!

				valorDecodificado = decodificador.getCurrentSymbolCount(
						TabelaIgnorancia.getInstance().getTotalSimbolosNaoDecodificados()	);

				simbolo = TabelaIgnorancia.getInstance().descobreSimbolo(valorDecodificado);
				
				System.out.println("simbolo encontrado na tabela de ignorancia " + simbolo);
	//			System.out.println(simbolo);


				dados = TabelaIgnorancia.getInstance().probSimboloDec(simbolo);
				
				this.retirarSimbolo(dados);

				if(simbolo2.length() < this.contextoMaximo) {
					simbolo2 = simbolo2 + simbolo;
				} else {
					simbolo2 = simbolo2.substring(1) + simbolo;
				}

				stringAux = simbolo2;
				tamanhoContextoAtivo = stringAux.length();
				

				System.out.println(simbolo2);

				p.adiciona(simbolo2);
				

			}
			else {
				String contextoReal;
				if (stringAux.length() == 1) {
					contextoReal = "";
					System.out.println("BUSQUEI CONTEXTO ''");
				}
				else {
					contextoReal = stringAux.substring(1, stringAux.length());
					System.out.println("BUSQUEI CONTEXTO " + stringAux.substring(1, stringAux.length()) );
				}
				
				No noContexto = p.retornarContexto(contextoReal);

				noContexto.exibir("");
				
//				if (noContexto.getFilhos() == null) {
//					System.out.println(">>>>>>>>>>>>>>>>>>>>if nao � inutil! qual a utlidade?!");
//					tamanhoContextoAtivo--;
//					System.out.println("tamanho string aux "+stringAux.length());
//					if (stringAux.length() <= 1) {
//						stringAux = "";
//						continue;
//					} else {
//						stringAux = stringAux.substring(1);
//					}
//				}
				
				int totalContexto = noContexto.totalDeSimbolosNoContexto();
				System.out.println("TOTAL DE SIMBOLOS NO CONTEXTO " + totalContexto);
				
				valorDecodificado = decodificar(totalContexto);
				
				System.out.println("VALOR DECODIFICADO " + valorDecodificado);
				
				String valorEncontradoArvore = noContexto.retornaSimbolo(valorDecodificado);
				
				System.out.println("VALOR STRING ENCONTRADA " + valorEncontradoArvore + "\n\n\n\n");
				dados = noContexto.retornaDadosAritmetico(valorEncontradoArvore);
				
				System.out.println("LOW " + dados[0] + " HIGH " + dados[1] + " TOTAL " + dados[2]);
				
				this.retirarSimbolo(dados);
				
				if(valorEncontradoArvore.equals("esc")) {
					System.out.println("ESC");
					tamanhoContextoAtivo--;
					if (stringAux.length() <= 1) {
						stringAux = "";
					} else {
						stringAux = stringAux.substring(1);
					}
				} else {
//					System.out.println(valorEncontradoArvore);

					simbolo = valorEncontradoArvore.charAt(valorEncontradoArvore.length()-1);
			//		System.out.println(simbolo);
					if(simbolo2.length() < this.contextoMaximo) {
						simbolo2 = simbolo2 + simbolo;
					} else {
						simbolo2 = simbolo2.substring(1) + simbolo;
					}

					stringAux = simbolo2;
					tamanhoContextoAtivo = stringAux.length() -1 ;

					System.out.println(simbolo2);
					p.adiciona(simbolo2);
				}
				
				
				
			}
			
			EasyIn in = new EasyIn();
			in.readChar();
			
			

		}
	}

	private void setArquivoSaida(String nomeArquivoSaida) {	
		try {
			outStream = new FileOutputStream(nomeArquivoSaida);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	private void setContextoMaximo(int tamContext) {
		this.contextoMaximo = tamContext;

	}

	private void setOriginalFile(String nomeArquivo) {

		try {
			BitInput input = new BitInput(new FileInputStream(new File(nomeArquivo)));
			decodificador = new ArithDecoder(input);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}	

	public static void main(String[] args) {
		
		EasyIn easy = new EasyIn();
/*
		System.out.print("Insira o nome do arquivo: ");
		String nomeArquivo = "";

		try{
			nomeArquivo = easy.readString();
		} catch(NumberFormatException e){
			System.out.println("ERRO: Arquivo 'meio.txt' ser� usado...");
			nomeArquivo = "meio.txt";
		}

		System.out.print("Insira o tamanho do Contexto: ");
		int tamContext;
		try{
			tamContext = easy.readInt();
		} catch(NumberFormatException e){
			System.out.println("ERRO: Contexto de tamanho 2 ser� usado...");
			tamContext = 2;
		}

		System.out.print("Insira o nome do arquivo de saida: ");
		String nomeArquivoSaida = "";

		try{
			nomeArquivoSaida = easy.readString();
		} catch(NumberFormatException e){
			System.out.println("ERRO: Arquivo 'default.txt' ser� usado como sa�da...");
			nomeArquivoSaida = "default.iti";
		}
		 */
		Decodificador.getInstance().setOriginalFile("abra3.iti");
		Decodificador.getInstance().setContextoMaximo(3);
		Decodificador.getInstance().setArquivoSaida("abra3.txt");
		Decodificador.getInstance().decodificar();

	}
}